using System;
using System.Collections.Generic;
using Gl.SettingsManager.Providers;
using System.Configuration;
using System.Reflection;
namespace Gl.SettingsManager
{
	public sealed class Settings
	{
		private ISettingsProvider _provider;
		private Dictionary<Type, ObjectPropertyManager> _managers = new Dictionary<Type, ObjectPropertyManager>();
		public ISettingsProvider SettingsProvider
		{
			get
			{
				if (!_provider.IsInitialized)
					_provider.InitializeProvider();
				return _provider;
			}
			set { _provider = value; }
		}

		internal Settings(ConfigurationFileSettingsProvider configProvider)
		{
			_provider = configProvider;
		}

		internal Settings(DataBaseSettingsProvider dbProvider)
		{
			_provider = dbProvider;
		}

		internal Settings(ISettingsProvider provider)
		{
			_provider = provider;
		}

		public static Settings GetSettingsWithCustomProvider(ISettingsProvider provider)
		{
			return new Settings(provider);
		}

		public static Settings GetSettingsWithConfigurationFileProvider(ConfigurationUserLevel userLevel)
		{
			ConfigurationFileSettingsProvider provider = new ConfigurationFileSettingsProvider();
			provider.UserLevel = userLevel;
			return new Settings(provider);
		}

		public static Settings GetSettingsWithDataBaseSettingsProvider(string connectionString)
		{
			if (string.IsNullOrEmpty(connectionString))
				throw new ArgumentNullException("connectionString");

			return GetSettingsWithDataBaseSettingsProvider(connectionString, true);
		}

		public static Settings GetSettingsWithDataBaseSettingsProvider(string connectionString,
			bool useTransactionWhenSaving)
		{
			if (string.IsNullOrEmpty(connectionString))
				throw new ArgumentNullException("connectionString");
			
			return GetSettingsWithDataBaseSettingsProvider(connectionString, useTransactionWhenSaving,
				Assembly.GetCallingAssembly().FullName);
		}

		public static Settings GetSettingsWithDataBaseSettingsProvider(string connectionString,
			bool useTransactionWhenSaving, string applicationName)
		{
			if (string.IsNullOrEmpty(connectionString))
				throw new ArgumentNullException("connectionString");

			if (string.IsNullOrEmpty(applicationName))
				throw new ArgumentNullException("applicationName");

			DataBaseSettingsProvider provider = new DataBaseSettingsProvider(connectionString,
				useTransactionWhenSaving, applicationName);
			return new Settings(provider);
		}

		/// <summary>
		/// Persists objects properties marked with SettingsProperty.
		/// </summary>
		/// <param name="obj">Operatable object</param>
		/// <param name="validate">Perform validation</param>
		public void Save(object obj, bool validate)
		{
			if (obj == null)
				throw new ArgumentNullException("obj");

			InternalSave(obj, validate);
		}

		/// <summary>
		/// Loads values to properties marked with SettingsProperty.
		/// </summary>
		/// <param name="obj">Operatable object</param>
		/// <param name="validate">Perform validation</param>
		public void Load(object obj, bool validate)
		{
			if (obj == null)
				throw new ArgumentNullException("obj");

			InternalLoad(obj, validate);
		}

		/// <summary>
		/// Persists objects properties marked with SettingsProperty.
		/// </summary>
		/// <param name="obj">Operatable object</param>
		public void Save(object obj)
		{
			if (obj == null)
				throw new ArgumentNullException("obj");

			InternalSave(obj, false);
		}

		/// <summary>
		/// Loads values to properties marked with SettingsProperty.
		/// </summary>
		/// <param name="obj">Operatable object</param>
		public void Load(object obj)
		{
			if (obj == null)
				throw new ArgumentNullException("obj");

			InternalLoad(obj, true);
		}

		#region Private Routine

		private void InternalSave(object obj, bool validate)
		{
			ObjectPropertyManager manager = GetManager(obj);
			SettingsCollection updatedCollection = manager.CollectSettingValues(obj, validate);
			SettingsCollection current =
				SettingsProvider.GetSettings(updatedCollection.TypeName, updatedCollection.ContextName);
			current.Copy(updatedCollection);
			SettingsProvider.Save();
		}

		private void InternalLoad(object obj, bool validate)
		{
			ObjectPropertyManager manager = GetManager(obj);
			SettingsCollection settings = 
				SettingsProvider.GetSettings(obj.GetType().FullName, Utils.GetContextName(obj));
			manager.InitializeObject(settings, obj, validate);
		}

		private ObjectPropertyManager GetManager(object obj)
		{
			Type objType = obj.GetType();
			if (!_managers.ContainsKey(objType))
				_managers.Add(objType, new ObjectPropertyManager(objType));

			return _managers[objType];
		}

		#endregion
	}
}
